package com.zkh.myframe.core;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zkh.myframe.annotation.Aspect;
import com.zkh.myframe.annotation.Config;
import com.zkh.myframe.annotation.Mapper;
import com.zkh.myframe.aspect.AspectProxy;
import com.zkh.myframe.aspect.AspectSuper;
import com.zkh.myframe.database.mapper.proxy.MapperProxy;
import com.zkh.myframe.exception.FrameException;
import com.zkh.myframe.request.Request;
import com.zkh.myframe.request.RequestProxy;
import com.zkh.myutils.bean.BeanArrayConverter;
import com.zkh.myutils.bean.WholeTypeConverter;
import com.zkh.myutils.proxy.Proxy;
import com.zkh.myutils.system.ConfigLoader;
import com.zkh.myutils.utils.Regexp;
import com.zkh.myutils.utils.StringUtils;

/**
 * 框架全局工具类
 * @author zkh
 */
public class FrameUtils {
	
	//存储Resource资源，如果使用单例模式，则从此Map中获取
	private static Map<Class<?>, Object> resourceMap = new ConcurrentHashMap<>();
	
	//HttpServletRequest和HttpServletResponse对象容器
	private static ThreadLocal<HttpServletRequest> servletRequests = new ThreadLocal<>();
	private static ThreadLocal<HttpServletResponse> servletResponses = new ThreadLocal<>();
	
	/**
	 * 获取HttpServletRequest对象
	 */
	public static HttpServletRequest getHttpServletRequest() {
		return servletRequests.get();
	}
	
	/**
	 * 获取HttpServletResponse对象
	 */
	public static HttpServletResponse getHttpServletResponse() {
		return servletResponses.get();
	}
	
	/**
	 * 获取链接后缀
	 */
	public static String getUrlSuffix(String uri) {
		//空或/结尾
		if(StringUtils.isEmpty(uri) || Regexp.isEndWith("/+", uri)) {
			return "";
		}
		//临时索引变量
		int i = uri.lastIndexOf("/");
		//存在
		if(i>-1) {
			uri = uri.substring(i+1);i=-1;
		}
		//检测后缀
		i = uri.lastIndexOf(".");
		//检测是否有后缀名
		if(i>-1 && !uri.endsWith(".")) {
			return uri.substring(i+1);
		}
		return "";
	}
	
	/**
	 * 创建一个Resource资源类（非单例）
	 * @param cls 类Class
	 */
	public static <T> T getResourceClass(Class<T> cls) {
		return getResourceClass(cls, false);
	}
	
	/**
	 * 获取一个Resource资源类
	 * @param cls 类Class
	 * @param isSingleton 是否使用单例获取资源类
	 */
	public static <T> T getResourceClass(Class<T> cls, boolean isSingleton) {
		return getResourceClass(cls, isSingleton, new HashMap<>());
	}
	
	/**
	 * 获取一个Resource资源类
	 * @param cls 类Class
	 * @param isSingleton 是否使用单例获取资源类
	 * @param tempResource 临时资源对象
	 */
	@SuppressWarnings("unchecked")
	private static <T> T getResourceClass(Class<T> cls, boolean isSingleton, Map<Class<?>, Object> tempResource) {
		try {
			//资源类
			Object instance = resourceMap.get(cls);
			//单例模式且有实例
			if(isSingleton && instance!=null) {
				return (T) instance;
			}
			//配置bean
			boolean config = cls.isAnnotationPresent(Config.class);
			//当前为配置bean且已有实例
			if(config && instance!=null) {
				return (T) instance;
			}
			//内部引用对象
			Object innerObj;
			//内部使用（内部已经实例化的对象直接使用，避免相互Resource导致异常）
			if((innerObj = tempResource.get(cls))!=null) {
				return (T) innerObj;
			}
			//创建新的资源类
			instance = getInstanceFromClass(cls, config);
			//缓存实例
			(isSingleton || config ? resourceMap : tempResource).put(cls, instance);
			//加载资源
			loadResource(cls, instance, isSingleton, tempResource);
			//返回实例
			return (T) instance;
		}catch(Exception e) {
			throw new FrameException("资源类创建失败", e);
		}
	}

	/**
	 * 创建实例
	 * @param clazz 待创建实例的类Class
	 */
	private static <T> T getInstanceFromClass(Class<T> clazz, boolean config) throws Exception {
		//配置类
		if(config) {
			return getInstanceFromConfigClass(clazz);
		}
		//切面注解
		Aspect aspectTemp;Aspect.List aspectListTemp = null;
		//如果是Mapper，则使用代理MapperProxy来处理
		if(clazz.isAnnotationPresent(Mapper.class)) {
			return Proxy.newProxyInstance(FrameUtils.class.getClassLoader(), clazz, new MapperProxy());
		}//内部请求
		else if(clazz.isAnnotationPresent(Request.class)) {
			return Proxy.newProxyInstance(FrameUtils.class.getClassLoader(), clazz, new RequestProxy(clazz));
		}
		//检查是否有切面注解
		else if((aspectTemp = clazz.getAnnotation(Aspect.class))!=null || 
				(aspectListTemp = clazz.getAnnotation(Aspect.List.class))!=null){
			//获取定义的切面
			Aspect[] aspectList = aspectTemp!=null ? new Aspect[] {aspectTemp} : aspectListTemp.value();
			//接收方法和处理器的容器
			List<String> methodPatternList = new ArrayList<>();
			List<AspectSuper> handlerList = new ArrayList<>();
			//代理方法pattern取并集
			StringBuilder sb = new StringBuilder();
			//遍历处理
			for(Aspect aspect: aspectList) {
				//代理方法正则
				if(!methodPatternList.contains(aspect.pattern())) {
					sb.append('(').append(aspect.pattern()).append(")|");
				}
				//保存代理正则和代理处理对象
				methodPatternList.add(aspect.pattern());
				handlerList.add(aspect.aspect().newInstance());
			}
			//创建切面代理
			AspectProxy aspectProxy = new AspectProxy(methodPatternList, handlerList);
			//生成代理
			return Proxy.newProxyInstance(FrameUtils.class.getClassLoader(), clazz, sb.substring(0, sb.length()-1), aspectProxy);
		}else{
			return clazz.newInstance();
		}
	}
	
	/**
	 * 创建一个配置类
	 * @param cls 配置实体类Class
	 */
	private static <T> T getInstanceFromConfigClass(Class<T> cls) throws InstantiationException, IllegalAccessException {
		//获取配置注解
		Config config = cls.getAnnotation(Config.class);
		//获取配置并转化为对象
		return ConfigLoader.getEntity(config.value(), cls, config.ignoreException());
	}

	/**
	 * 加载类所包含的资源
	 * @param clazz 实例类的class
	 * @param instance 实例
	 */
	private static void loadResource(Class<?> clazz, Object instance, boolean isSingleton, Map<Class<?>, Object> tempResource) throws Exception {
		//是否是代理类，代理类包含_proxyHandler字段
		boolean isProxy = readerMethod(clazz.getDeclaredFields(), instance, isSingleton, tempResource);
		//如果是代理类，还要处理父类方法
		if(isProxy) {
			readerMethod(clazz.getSuperclass().getDeclaredFields(), instance, isSingleton, tempResource);
		}
	}
	
	/**
	 * 遍历方法
	 */
	private static boolean readerMethod(Field[] fields, Object instance, boolean isSingleton, Map<Class<?>, Object> tempResource) throws Exception {
		//是否是代理类，代理类包含_proxyHandler字段
		boolean isProxy = false;
		//检查方法上是否有Resource注解
		outerLoop: for(Field field: fields){
			//判断是否是代理类
			isProxy = isProxy || "_proxyHandler".equals(field.getName());
			//设置可访问
			field.setAccessible(true);
			//如果存在Resource注解
			if(field.isAnnotationPresent(Resource.class)){
				//获取资源名称
				String resource = field.getType().getName();
				//注入的类的class
				Class<?> resourceClass = Class.forName(resource);
				//注入的类的实例
				Object target = getResourceClass(resourceClass, isSingleton, tempResource);
				//注入值
				field.set(instance, target);
			}//读取配置
			else if(field.isAnnotationPresent(Config.class)) {
				//字段类型
				Class<?> type = field.getType();
				String typeName = type.getName();
				//读取注解配置
				Config config = field.getAnnotation(Config.class);
				//配置Key值
				String value = config.value();
				//匹配变量正则
				String regexp = "\\$\\{[^}]+\\}";
				//如果值是单个表达式，如：@Config("${config.key1}")，可转换复杂类型的配置
				if(value.matches("^" + regexp + "$")) {
					//配置Key
					String key = value.substring(2, value.length()-1);
					//分隔符
					int sep = key.indexOf(':');
					//Key
					String realKey = sep>-1 ? key.substring(0, sep) : key;
					//字符串转换类型
					if(WholeTypeConverter.TYPE_WHOLE_LIST.contains(typeName)) {
						//获取配置值
						String configValue = ConfigLoader.getString(realKey, config.ignoreException());
						//没有配置
						if(configValue==null && (configValue = sep>-1 ? key.substring(sep+1) : null)==null) {
							//抛异常
							if(!config.ignoreException()) {
								throw new NullPointerException("未读取到配置：" + value);
							}
							//跳过本字段赋值
							continue;
						}
						//设置字符值
						field.set(instance, castType(configValue, typeName, "不支持将值" + configValue + "转换到类型为" + typeName + "的字段" + instance.getClass().getName() + "." + field.getName() + "上"));
					}//数组类型
					else if(type.isArray()) {
						//读取配置
						List<Object> list = ConfigLoader.getList(realKey, config.ignoreException());
						//转换配置
						field.set(instance, list==null ? null : BeanArrayConverter.toArray(list, list.getClass(), type));
					}//集合类型
					else if(List.class.isAssignableFrom(type)){
						//保存数据
						field.set(instance, ConfigLoader.getEntityList(realKey, type, field.getGenericType().getTypeName(), config.ignoreException()));
					}//Map类型
					else if(Map.class.isAssignableFrom(type)) {
						//保存数据
						field.set(instance, ConfigLoader.getEntityMap(realKey, type, field.getGenericType().getTypeName(), config.ignoreException()));
					}
					//其它默认为对象转换
					else {
						field.set(instance, ConfigLoader.getEntity(realKey, type, config.ignoreException()));
					}
				}//如果是表达式拼接，如：@Config("${config.rootpath}/dir/${config.path.img}")或@Config("${config.numberPrefix}/0000")，可转换字符串、数字和Boolean值
				else {
					//匹配替换字符
					Set<String> matchSet = new HashSet<>(Regexp.getMatchesAll(regexp, value));
					//遍历替换值
					for(String curr: matchSet) {
						//配置Key
						String key = curr.substring(2, curr.length()-1);
						//分隔符
						int sep = key.indexOf(':');
						//默认值
						String dftVal = sep>-1 ? key.substring(sep+1) : null;
						//获取配置值
						String configValue = ConfigLoader.getString(sep>-1 ? key.substring(0, sep) : key, config.ignoreException());
						//没有配置
						if(configValue==null && (configValue = dftVal)==null) {
							//跳过本字段赋值
							continue outerLoop;
						}
						//替换变量值
						value = value.replace(curr, configValue);
					}
					//设置字符值
					field.set(instance, castType(value, typeName, "不支持将值" + value + "转换到类型为" + typeName + "的字段" + instance.getClass().getName() + "." + field.getName() + "上"));
				}
			}
		}
		return isProxy;
	}
	
	/**
	 * 转换字符类型
	 * @param value 待转换的字符串
	 * @param typeName 要转换的类型名称
	 * @param throwMessage 未匹配到类型，抛出异常信息。为空不抛出异常
	 * @return 转换后的值
	 */
	static Object castType(String value, String typeName, String throwMessage) {
		//根据不同类型转换值类型
		switch (typeName) {
			case "String":
				return value;
			case "Boolean":
			case "boolean":
				return "true".equals(value);
			case "Long":
			case "long":
				return Long.parseLong(value);
			case "Integer":
			case "int":
				return Integer.parseInt(value);
			case "Short":
			case "short":
				return Short.parseShort(value);
			case "Byte":
			case "byte":
				return Byte.parseByte(value);
			case "Double":
			case "double":
				return Double.parseDouble(value);
			case "Float":
			case "float":
				return Float.parseFloat(value);
			case "Character":
			case "char":
				return value.charAt(0);
			case "BigDecimal":
				return new BigDecimal(value);
			case "BigInteger":
				return new BigInteger(value);
			default:
				throw new UnsupportedOperationException(throwMessage);
		}
	}
 
	/**
	 * 获取HttpServletRequest对象容器
	 */
	static ThreadLocal<HttpServletRequest> getServletRequestContainer() {
		return servletRequests;
	}
	
	/**
	 * 获取HttpServletResponse对象容器
	 */
	static ThreadLocal<HttpServletResponse> getServletResponseContainer() {
		return servletResponses;
	}
}
